#include <stdlib.h>
#include <stdio.h>

#include "aceMesh_runtime_c.h"
#include "fdtd_aceMesh.h"
#include "slave.h"
#include "float.h"
#include "simd.h"

extern double ex[NX][NY+1];
extern double ey[NX+1][NY];
extern double hz[NX][NY];

//core region
__thread_local_fix double slave[3][TZ1][TZ2];//three ldm array
//shade region
__thread_local_fix double slave_hz_left[TZ1];//hz[i][j-1]
__thread_local_fix double slave_hz_up[TZ2];//hz[i-1][j]
__thread_local_fix double slave_ex_right[TZ1];//ex[i][j+1]
__thread_local_fix double slave_ey_down[TZ2];//ey[i+1][j]
__thread_local volatile unsigned long get_reply,put_reply;

#define min(a,b) ((a>b)?(b):(a))
#define max(a,b) ((a>b)?(a):(b))

#ifdef PROFILING
__thread_local_fix int reuse_count=0;
__thread_local_fix volatile unsigned long comp_start,comp_end;
__thread_local_fix volatile unsigned long dma_get_end,dma_get_start,dma_put_end,dma_put_start,ldm_dma_get_end,ldm_dma_get_start;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_ldm_dma_count[64];
extern unsigned long slave_dma_count[64];
extern unsigned long slave_reuse_count[64];
unsigned long rtc_()
{
    unsigned long rpcc;
    asm volatile("rcsr  %0,4":"=r"(rpcc));
    return rpcc;
}
#endif

//loop1
void stencil_loop1(targs1 *args1)
{
  int ey_no;
  int j;
  int t_y=args1->t;
  int jj=args1->jj;
  int tileno[2];
  int varno[1]; 
  int reuse[1];
  //printf("stencil core is begin\n");
#ifdef PROFILING
  ldm_dma_get_start=rtc_();
#endif
#ifdef _LDMM
  tileno[0]=0/TZ1;
  tileno[1]=jj/TZ2; 
  //slave_ldm_postreq_tile(args1->loop_id, &ey[0][0], tileno, OUT);//
  slave_ldm_postreq_tile(&ey[0][0], 2, tileno, OUT);//
  slave_ldm_getreq_tile(varno,reuse);
  ey_no=varno[0];
#endif
  //printf("loop1_ey-no:%d\n",ey_no);
#ifdef PROFILING
  ldm_dma_get_end=comp_start=rtc_();
#endif
  //compute
  for(j=0;j<min(NY,TZ2);j++)
      slave[ey_no][0][j]=t_y;
#ifdef PROFILING
  comp_end=rtc_();
  dma_put_start=rtc_();
#endif
  //write back
  put_reply=0;
  athread_put(PE_MODE
        , &slave[ey_no][0][0], &ey[0][jj]
        , TZ2*8, (void *)&put_reply, 0, 0); 
  while(put_reply!=1);
#ifdef PROFILING
  dma_put_end=rtc_();
  slave_comp_count[_MYID]=slave_comp_count[_MYID]+(comp_end-comp_start);
  slave_dma_count[_MYID]=slave_dma_count[_MYID]+(dma_put_end-dma_put_start);
  slave_ldm_dma_count[_MYID]=slave_ldm_dma_count[_MYID]+(dma_put_end-dma_put_start)+(ldm_dma_get_end-ldm_dma_get_start);
#endif
}


//loop2
void stencil_loop2(targs2 *args2)
{
  int tileno[2];
  int varno[2]; 
  int reuse[2];
  //int slave_i;
  int tsize, bsize, stride;
  int ey_no,hz_no;
  int dma_num = 0;
  int ii=args2->ii;
  int jj=args2->jj;
  int i,j,k;
  //printf("stencil core is begin2\n");
  //fflush(stdout);
#ifdef MMEM
  for(i=max(ii,1);i<min(NX,ii+TZ1);i++)
   for(j=jj;j<min(NY,jj+TZ2);j++)
     ey[i][j] = ey[i][j] - 0.5*(hz[i][j] - hz[i-1][j]);
#else
#ifdef PROFILING
  ldm_dma_get_start=rtc_();
#endif
  tileno[0]=ii/TZ1;
  tileno[1]=jj/TZ2;
  slave_ldm_postreq_tile(&ey[0][0], 2, tileno, OUT);//
  slave_ldm_postreq_tile(&hz[0][0], 2, tileno, IN);
  slave_ldm_getreq_tile(varno,reuse);
  ey_no = varno[0];
  hz_no = varno[1]; 
#ifdef PROFILING    
  dma_get_start=rtc_();
#endif
  //printf("loop2-ey_no:%d,hz_no:%d\n",ey_no,hz_no);
  get_reply=0;
   //ey
  if(ey_no <0) 
    printf("Array assignment failed\n");
  else if(reuse[0]==0)
  {
    // printf("fetch loop2-ey-data,loop_id=%d,ii=%d,jj=%d\n",args2->loop_id,ii,jj);
     //fetch ey data
     bsize=TZ2*8;
     stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
     tsize= bsize*TZ1;
     athread_get(PE_MODE,&ey[ii][jj]
              , &slave[ey_no][0][0]
              , tsize, (void *)&get_reply
              , 0, stride, bsize);
     dma_num++;
  }
  //hz
  if(hz_no<0)
    printf("Array assignment failed\n");
  else if(reuse[1]==0)
  {
     //fetch hz data
    // printf("fetch loop2-hz-data,loop_id=%d,ii=%d,jj=%d\n",args2->loop_id,ii,jj); 
     bsize=TZ2*8;
     stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
     tsize= bsize*TZ1;
     athread_get(PE_MODE,&hz[ii][jj]
              , &slave[hz_no][0][0]
              , tsize, (void *)&get_reply
              , 0, stride, bsize);
     dma_num++;
  }
  if(max(ii,1)!=1)
  {
    //update hz_up_data hz[i-1][]
    athread_get(PE_MODE,&hz[ii-1][jj],&slave_hz_up[0],TZ2*8,(void *)&get_reply,0,0,0); 
    dma_num++;
  }
  while(get_reply!=(dma_num));
#ifdef PROFILING  
   ldm_dma_get_end = dma_get_end = comp_start = rtc_();
#endif

//compute
  for(i=1;i<TZ1;i++)
    for(j=0;j<TZ2;j++) 
      slave[ey_no][i][j] = slave[ey_no][i][j] - 0.5*(slave[hz_no][i][j] - slave[hz_no][i-1][j]);
  if(max(ii,1)!=1)
  {
    for(j=0;j<TZ2;++j)
      slave[ey_no][0][j] = slave[ey_no][0][j] - 0.5*(slave[hz_no][0][j] - slave_hz_up[j]);
  }
#ifdef PROFILING    
  comp_end = dma_put_start = rtc_();
#endif
  //write back
  put_reply=0;
  bsize=TZ2*8;
  stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead
  tsize= bsize*TZ1;
  athread_put(PE_MODE
        ,&slave[ey_no][0][0], &ey[ii][jj]
        ,tsize,(void *)&put_reply
        ,stride,bsize);
  while(put_reply!=1);
#ifdef PROFILING    
  dma_put_end=rtc_();
  slave_comp_count[_MYID]=slave_comp_count[_MYID]+(comp_end-comp_start);
  slave_dma_count[_MYID]=slave_dma_count[_MYID]+(dma_get_end-dma_get_start)+(dma_put_end-dma_put_start);    
  slave_ldm_dma_count[_MYID]=slave_ldm_dma_count[_MYID]+(ldm_dma_get_end-ldm_dma_get_start)+(dma_put_end-dma_put_start);    
#endif

#endif
}


//loop3
void stencil_loop3(targs2 *args3)
{
  int tileno[2];
  int varno[2]; 
  int reuse[2];
  int ii=args3->ii;
  int jj=args3->jj;
  int i,j,k;
  int tsize, bsize, stride;
  int ex_no,hz_no;
  int dma_get_num = 0;
  int dma_put_num = 0;
#ifdef MMEM
  for (i=ii; i<min(NX,ii+TZ1); i++)
    for(j = max(jj,1); j < min(NY,jj+TZ2); j++)
      ex[i][j] = ex[i][j] - 0.5*(hz[i][j] - hz[i][j-1]);
#else
#ifdef PROFILING
  ldm_dma_get_start=rtc_();
#endif
  tileno[0]=ii/TZ1;
  tileno[1]=jj/TZ2;
  slave_ldm_postreq_tile(&ex[0][0], 2, tileno, OUT);//
  slave_ldm_postreq_tile(&hz[0][0], 2, tileno, IN);
  slave_ldm_getreq_tile(varno,reuse);
  ex_no = varno[0];
  hz_no = varno[1];
#ifdef PROFILING    
  dma_get_start=rtc_();
#endif

//printf("loop3-ex-no:%d,hz-no:%d\n",ex_no,hz_no);
  get_reply=0;
  //fetch ex data
  if(ex_no <0) 
    printf("Array assignment failed\n");
  else if(reuse[0]==0)
  {
    //printf("fetch loop3-ex-data,loop_id=%d,ii=%d,jj=%d\n",args3->loop_id,ii,jj);
    bsize=TZ2*8;
    stride=(NY+1-TZ2)*8;
    tsize= bsize*TZ1;
    athread_get(PE_MODE,&ex[ii][jj]
              , &slave[ex_no][0][0]
              , tsize, (void *)&get_reply
              , 0, stride, bsize); 
    dma_get_num++;
  }
  //fetch hz data
  if(hz_no<0)
    printf("Array assignment failed\n");
  else if(reuse[1]==0)
  {
  //  printf("fetch loop3-hz-data,loop_id=%d,ii=%d,jj=%d\n",args3->loop_id,ii,jj);
    bsize=TZ2*8;
    stride=(NY-TZ2)*8; 
    tsize= bsize*TZ1;
    athread_get(PE_MODE,&hz[ii][jj]
              , &slave[hz_no][0][0]
              , tsize, (void *)&get_reply
              , 0, stride, bsize);
    dma_get_num++;
  }
  if(max(jj,1)!=1)
  {
     //update hz_left_data hz[i][j-1]
     bsize=8;
     stride=(NY-1)*8; 
     tsize= bsize*TZ1;
     athread_get(PE_MODE,&hz[ii][jj-1]
              , &slave_hz_left[0]
              , tsize, (void *)&get_reply
              , 0, stride, bsize); 
     dma_get_num++;
  }
  while(get_reply!=dma_get_num);  
#ifdef PROFILING 
  ldm_dma_get_end = dma_get_end = comp_start = rtc_();
#endif

  //compute
  for( i= 0; i < TZ1; i++)
    for(j = 1; j < TZ2; j++)
       slave[ex_no][i][j] = slave[ex_no][i][j] - 0.5*(slave[hz_no][i][j] - slave[hz_no][i][j-1]);
  if(max(jj,1)!=1)
    for( i= 0; i < TZ1; i++)
      slave[ex_no][i][0] = slave[ex_no][i][0] - 0.5*(slave[hz_no][i][0] - slave_hz_left[i]);
#ifdef PROFILING    
  comp_end=rtc_();
  dma_put_start=rtc_();
#endif
  //write back
  put_reply=0;
/*  if(max(jj,1)==1)
  {
    for(i=0;i<TZ1;++i)
      athread_put(PE_MODE,&slave[ex_no][i][1]
              , &ex[ii+i][1]
              ,(TZ2-1)*8,(void *)&put_reply,0,0);  
    dma_put_num+=TZ1;
  }else{*/
    bsize=(TZ2)*8;
    stride=(NY+1-(TZ2))*8; 
    tsize= bsize*TZ1;
    athread_put(PE_MODE
        ,&slave[ex_no][0][0], &ex[ii][jj]
        ,tsize,(void *)&put_reply
        ,stride,bsize); 
//    dma_put_num++;
//  }
  while(put_reply!=1);
#ifdef PROFILING    
  dma_put_end=rtc_();
  slave_comp_count[_MYID]=slave_comp_count[_MYID]+(comp_end-comp_start);
  slave_dma_count[_MYID]=slave_dma_count[_MYID]+(dma_get_end-dma_get_start)+(dma_put_end-dma_put_start);    
  slave_ldm_dma_count[_MYID]=slave_ldm_dma_count[_MYID]+(ldm_dma_get_end-ldm_dma_get_start)+(dma_put_end-dma_put_start);    
#endif
#endif
}


//loop4
void stencil_loop4(targs2 *args4)
{
  int tileno[2];
  int varno[3]; 
  int reuse[3];
  int i,j;
  int ii=args4->ii;
  int jj=args4->jj;
  int tsize, bsize, stride;
  int ex_no, ey_no,hz_no;
  int dma_num = 0;
#ifdef MMEM
  for(i=ii; i<min(NX,ii+TZ1); i++)
    for(j=jj; j<min(NY,jj+TZ2); j++)
      hz[i][j] = hz[i][j] - 0.7*(ex[i][j+1] - ex[i][j] + ey[i+1][j] - ey[i][j]);
#else
#ifdef PROFILING
  ldm_dma_get_start=rtc_();
#endif
  tileno[0]=ii/TZ1;
  tileno[1]=jj/TZ2;
  slave_ldm_postreq_tile(&ex[0][0], 2, tileno, IN);//
  slave_ldm_postreq_tile(&ey[0][0], 2, tileno, IN);
  slave_ldm_postreq_tile(&hz[0][0], 2, tileno, OUT);
  slave_ldm_getreq_tile(varno,reuse);
  ex_no = varno[0];
  ey_no = varno[1];
  hz_no = varno[2];
  //printf("loop4-ex-no:%d,ey-no:%d,hz-no:%d\n",ex_no,ey_no,hz_no);
#ifdef PROFILING    
  dma_get_start=rtc_();
#endif
//fetch ex  data 
  get_reply=0;
  if(ex_no <0)
    printf("Array assignment failed\n");
  else if(reuse[0]==0)
  {
   // printf("fetch loop4-ex-data,loop_id=%d,ii=%d.jj=%d\n",args4->loop_id,ii,jj);
    bsize=(TZ2)*8;
    stride=(NY+1-TZ2)*8;  //stride: from blocktail to blockhead 
    tsize= bsize*(TZ1);
    athread_get(PE_MODE,&ex[ii][jj]
              , &slave[ex_no][0][0]
              , tsize, (void *)&get_reply
              , 0, stride, bsize);
    ++dma_num;
  }
  //fetch ey data 
  if(ey_no <0)
    printf("Array assignment failed\n");
  //else if(ii==TZ1||jj==TZ2||reuse[1]==0)                  
  else if(reuse[1]==0)//||ii==TZ1||jj==TZ2)
  {
    //printf("fetch loop4-ey-data,loop_id=%d,ii=%d,jj=%d\n",args4->loop_id,ii,jj);
    bsize=TZ2*8;
    stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
    tsize= bsize*(TZ1);
    athread_get(PE_MODE,&ey[ii][jj]
              , &slave[ey_no][0][0]
              , tsize, (void *)&get_reply
              , 0, stride, bsize);
    ++dma_num;
  }
  //fetch hz data
  if(hz_no <0)
    printf("Array assignment failed\n");
  else if(reuse[2]==0)
  {
    //printf("fetch loop4-hz-data,loop_id = %d,ii=%d,jj=%d\n",args4->loop_id,ii,jj);
    bsize=TZ2*8;
    stride=(NY-TZ2)*8;  //stride: from blocktail to blockhead 
    tsize= bsize*(TZ1);
    athread_get(PE_MODE,&hz[ii][jj]
              , &slave[hz_no][0][0]
              , tsize, (void *)&get_reply
              , 0, stride, bsize);
    ++dma_num;
  }
  //fetch slave_ex_data
  bsize=8;
  stride=(NY+1-1)*8;  //stride: from blocktail to blockhead 
  tsize= bsize*(TZ1);
  athread_get(PE_MODE,&ex[ii][jj+TZ2]
              , &slave_ex_right[0]
              , tsize, (void *)&get_reply
              , 0, stride, bsize);
  ++dma_num;
  //fetch slave_ey_data
  athread_get(PE_MODE,&ey[ii+TZ1][jj],&slave_ey_down[0],TZ2*8,(void *)&get_reply,0,0,0);
  ++dma_num;
  while(get_reply!=dma_num);
#ifdef PROFILING    
  ldm_dma_get_end = dma_get_end = comp_start = rtc_();
#endif
  //compute
  for(i=0; i<TZ1-1; i++)
    for(j=0; j<TZ2-1; j++)
      slave[hz_no][i][j] = slave[hz_no][i][j]-\
                0.7*(slave[ex_no][i][j+1] - slave[ex_no][i][j]\
                + slave[ey_no][i+1][j] - slave[ey_no][i][j]);
  for(j = 0; j < TZ2-1; j++)
    slave[hz_no][TZ1-1][j] = slave[hz_no][TZ1-1][j] -\
                0.7*(slave[ex_no][TZ1-1][j+1] - slave[ex_no][TZ1-1][j]\
                + slave_ey_down[j] - slave[ey_no][TZ1-1][j]); 
  for(i=0; i<TZ1-1; i++)
    slave[hz_no][i][TZ2-1] = slave[hz_no][i][TZ2-1] -\
                0.7*(slave_ex_right[i]- slave[ex_no][i][TZ2-1]\
                + slave[ey_no][i+1][TZ2-1] - slave[ey_no][i][TZ2-1]);
  slave[hz_no][TZ1-1][TZ2-1] = slave[hz_no][TZ1-1][TZ2-1]-\
                0.7*(slave_ex_right[TZ1-1] - slave[ex_no][TZ1-1][TZ2-1]\
                +slave_ey_down[TZ2-1]- slave[ey_no][TZ1-1][TZ2-1]);
#ifdef PROFILING    
  comp_end= dma_put_start = rtc_();
#endif

//write back
  put_reply=0;
  bsize=TZ2*8;
  stride=(NY-TZ2)*8;
  tsize= bsize*TZ1;
  athread_put(PE_MODE
        ,&slave[hz_no][0][0], &hz[ii][jj]
        ,tsize,(void *)&put_reply
        ,stride,bsize);
  while(put_reply!=1);
#ifdef PROFILING    
  dma_put_end=rtc_();
  slave_comp_count[_MYID]=slave_comp_count[_MYID]+(comp_end-comp_start);
  slave_dma_count[_MYID]=slave_dma_count[_MYID]+(dma_get_end-dma_get_start)+(dma_put_end-dma_put_start);
  slave_ldm_dma_count[_MYID]=slave_ldm_dma_count[_MYID]+(ldm_dma_get_end-ldm_dma_get_start)+(dma_put_end-dma_put_start);    
#endif
#endif
}
